#include "Chunk.h"
#include <fstream>

int _Chunk::Setup(char * a_File, Block * a_G,int a_X,int a_Y, int a_H, int a_W) {
	FileName = a_File;
	Grid = a_G;
	X = a_X;
	Y = a_Y;
	H = a_H;
	W = a_W;
	ChunkChanges = new Vector<WorldChange*>;
	return SUCCEED;
}

int _Chunk::Clear() {
	FileName = NULL;
	Grid = NULL;
	if(ChunkChanges) delete ChunkChanges;
	ChunkChanges = NULL;
	return SUCCEED;
}

#ifdef _DRAW_COLLIDE_GENERATE_
int _Chunk::TempLoad() {
	return Load(new Block[50 * 50]);
}
#endif

int _Chunk::Load(Block * pBlockGrid) {
	Grid = pBlockGrid;
	return LoadGridFromFile(FileName);
}

int _Chunk::Unload() {
	int r = SaveGridToFile(FileName);
	Grid = NULL;
	return r;
}

int _Chunk::Save() {
	return SaveGridToFile(FileName);
}

int _Chunk::LoadGridFromFile(){
	return LoadGridFromFile(FileName);
}

int _Chunk::LoadGridFromFile(char * F) {
	//Check the memory
	if(!F)return FAIL_LOSS_OF_MEMORY;
	if(!Grid)return FAIL_LOSS_OF_MEMORY;

	static std::ifstream in;
	in.open(F,std::ios::in);
	
	// Added check to make sure file exists before attempting to load from it - Michael
	if(!in.is_open())return FAIL_FILE_NOT_AVAILABLE;

	static int count, x, y,buffer,BlockBuffer,ByteCount;
	x = W-1;
	y = H-1;

	//This reads the char amount of the file, but loading doesnt care about it, so its just being used
	// as a temporary dummy cell.
	in >> count;
	while ( !in.eof() ) {
		//Reads how many blocks of the same type
		in >> count;

		BlockBuffer = 0;
		ByteCount = 4;
		////Reads the blocks information byte by byte
		do{
			// Reads the current byte
			in >> buffer;
			// Puts the data in buffer in the first byte slot first
			// (c = 4) -> ( c - 4 * -1 ) = 0
			// (c = 3) -> ( c - 4 * -1 ) = 1
			// ...

			BlockBuffer |= ( buffer << ( ( ByteCount - 4 ) * -1 ) * 8 );
			ByteCount--;
		}while ( ByteCount > 0 );
		
		for(int i = count;i > 0 && y >= 0;--x,--i) {
			//Stores all the data
			Grid[x + (y * H)].SetData_s(BlockBuffer);
			if ( x <= 0 )
			{
				x = W;
				y--;
			}
		}
	}
	in.close();
	if(!F)return FAIL_LOSS_OF_MEMORY;
	if(!Grid)return FAIL_LOSS_OF_MEMORY;
	return SUCCEED;
}

int const _Chunk::SaveGridToFile() {
	return SaveGridToFile(FileName);
}

void SaveBlocks(std::ofstream & out,unsigned int * ibuffer,Block * B,short * CharAmount) {
	//Used for precise number division
	static float DummyCell;
	static char DummyStr[7];
	////Writes how many blocks of this type are being saved
	out << " " << (*ibuffer);

	DummyCell = (float)(*ibuffer);
	_itoa_s((int)DummyCell,DummyStr,7,10);
	
	//Adds one because of the space between numbers
	(*CharAmount) += strlen(DummyStr) + 1;
	

	(*ibuffer) = B->GetData_s();
	for(int i = 4-1;i >= 0;--i) {
		//Only writes the first byte of data
		DummyCell = (float)( (*ibuffer) & FIRST_BYTE_MASK );
		out << " " << (int)DummyCell;
		_itoa_s((int)DummyCell,DummyStr,7,10);
		(*CharAmount) += strlen(DummyStr) + 1;
		//Shifts the data over one byte so 
		// NULL-4th-3rd-2nd (making 2nd the new first byte)
		// NULL-NULL-4th-3rd
		(*ibuffer) = (*ibuffer) >> 8;
	}
}

int _Chunk::SaveGridToFile(char * F) {
	if(!F)return FAIL_LOSS_OF_MEMORY;
	if(!Grid)return FAIL_LOSS_OF_MEMORY;

	//Currently this method only writes blocks to the file
	static int x,y;
	x = W-1;
	y = H-1;

	static unsigned int ibuffer;
	ibuffer = 1;
	//This number is to confirm that it never tries to write more blocks to the file than can be
	// in a chunk at a time
	static short Blockcount,
		//This counts how many characters are witten to the file so that networking can optimize its sending method
		CharAmount;
	Blockcount = H * W - 1;
	CharAmount = 0;
	//The current block information being written
	static Block * ToWrite;

	
	
	

	static std::ofstream out;
	out.open ( F , std::ios::out );

	//This writes a space to store the number of characters written to the file
	// The number of characters is written at the end of the function
	out.write( "      ", 6 );
	CharAmount = 6;

	
	//Steps through every block
	for(int x = W-1,y = H-1;y >= 0,Blockcount > 0;--x,Blockcount--) {
		//Set the Block to the current block being looked at.
		ToWrite = &Grid[x + (y * H)];

		//if the next block is valid and not equal to the current block
		if ( x + (y * H) - 1 >= 0 && (*ToWrite) != Grid[x + (y * H) - 1] ) {
			//Write how many of them have been equal to each other
			SaveBlocks(out,&ibuffer,ToWrite,&CharAmount);
		}
		ibuffer++;

		if ( x <= 0 ) {
			y--; x = W;
		}
	}
	ToWrite = &Grid[0];
	SaveBlocks(out,&ibuffer,ToWrite,&CharAmount);
	
	//Write character amount
	out.seekp(1);
	static char Buffer[7];
	Buffer[0] = '\0';
	_itoa_s(CharAmount,Buffer,7,10);
	out.write(Buffer,strlen(Buffer));

	out.close();

	if(!F)return FAIL_LOSS_OF_MEMORY;
	if(!Grid)return FAIL_LOSS_OF_MEMORY;
	return SUCCEED;
}

void _Chunk::FinishAllChanges() {
	bool SafteyNet = true;
	while( ChunkChanges->size() && SafteyNet) {
		SafteyNet = FinishOldestChange();
	}
}

bool _Chunk::FinishOldestChange() {
	WorldChange * OldestChange = ChunkChanges->getByIndex(0);
	if(Grid) {
		if(!Changed) Changed = true;
		int x = 0,y = 0,tx = CHUNK_SIZE_W,ty = CHUNK_SIZE_H;
		x = OldestChange->x;
		y = -1 * OldestChange->y;
		//This is the proper conversion from collision data to array position data
		
		x = ((x - GetPosModX()) / BLOCK_TEXTURE_SIZE) - 1;
		if(x < 0) x = 0;
		y = ((y - GetPosModY()) / BLOCK_TEXTURE_SIZE) - 1;
		if(y < 0) y = 0;

		if( Grid[x + (y * CHUNK_HEIGHT)].GetData_s() &&
			Grid[x + (y * CHUNK_HEIGHT)].GetTrait_s() != BLOCK_TRAIT_UNBREAKABLE) {
			Grid[x + (y * CHUNK_HEIGHT)].SetData_s(OldestChange->Data);
		}
		ChunkChanges->remove(0);
		delete OldestChange;
		return true;
	}
	return false;
}